import os,re,datetime,cherrypy,StringIO,subprocess
from sqlalchemy import *
from sqlalchemy.orm import *
from elixir import *
from elixir.options import options_defaults
from PIL import Image, ImageDraw
from xml.dom.minidom import parse
from util import *
import math

basepath = "E:\\Work\\cap\\branches"
con = ''
for i in open(os.path.join(basepath,'dev.cfg')):
    if i.startswith('sqlalchemy.dburi='):
        con = i[i.find('"')+1:-2]
        break
metadata.bind = create_engine(con,pool_recycle=3600)

def flush():
    try:
        session.flush()
    except:
        session.rollback()
        
options_defaults['tablename'] = lambda cls:re.sub('([A-Za-z])([A-Z][a-z])','\\1_\\2',cls.__name__).lower()
    
class Photo(Entity):
    picbaseurl = "http://http://www.hotornot.com/r/?eid="
    uid = Field(String(50),default='')
    img = Field(Text, deferred=True)
    roi = Field(Text, deferred=True)
    score = Field(Float)
    feature = OneToOne('FacialFeature')
    position = OneToOne('FacePosition')
    test = Field(Integer, default=-1)
    time = Field(DateTime, default=datetime.datetime.now())

    @classmethod
    def createPhoto(cls, file):
        im = Image.open(file)
        a = StringIO.StringIO()
        im.save(a,'JPEG')
        uid = genkey()
        p = cls(uid=uid, img=a.getvalue(), score=-1.0)
        flush()
        if p.recognize():
            print 'recd'
            return uid
        else:
            print 'not recd'
            return None
        
    def destroySelf(self):
        if self.feature:
            self.feature.delete()
        if self.position:
            self.position.delete()
        self.delete()
        
    def getFileName(self):
        return self.uid + '.jpg'
    
    def getFilePath(self):
        return os.path.join(basepath,'cap','photos',self.getFileName())
    
    def output(self):
        targetfn = self.getFilePath()
        img = Image.open(StringIO.StringIO(self.img))
        img.save(targetfn, "JPEG")
        
    def remove(self):
        os.remove(self.getFilePath())
        
    def recognize(self):
        detectorpath = os.path.join(basepath,'cap','detector','detect.exe')
        self.output()
        parameter = self.getFilePath()
        s = subprocess.Popen(detectorpath + ' ' + parameter, stdout=subprocess.PIPE)
        out = s.stdout.read()
        if out[0] != '0':
            self.remove()
            self.destroySelf()
            return 0
        else:    #recognized
            xmlfile = self.getFilePath().replace('.jpg','.xml')
            dom = parse(xmlfile)
            angle = float(getValue(dom,'angle'))
            w = float(getValue(dom,'w'))
            center = eval(getValue(dom,'center'))
            tl = (round(center[0] - w/2.0), round(center[1] - w/2.0))
            tr = (round(center[0] + w/2.0), round(center[1] - w/2.0))
            bl = (round(center[0] - w/2.0), round(center[1] + w/2.0))
            br = (round(center[0] + w/2.0), round(center[1] + w/2.0))
            FacePosition(photo=self, angle=angle, w=w, center=center, tl=tl, tr=tr, bl=bl, br=br)
            ff = dict()
            facex = []
            facey = []
            for k in FacialFeature.names.values():
                ff[k] = eval(getValue(dom,k))
                if k.startswith('FSDKP_FACE_CONTOUR'):
                    facex.append(ff[k][0])
                    facey.append(ff[k][1])
            ang = 1.0/math.cos(math.radians(angle))
            st = (max(facey) - min(facey)) * ang
            head_height = getStandard((max(facey) - min(facey)) * ang, st)
            head_width = getStandard((max(facex) - min(facex)) * ang, st)
            left_eye_height = getStandard((max(facey) - ff['FSDKP_LEFT_EYE'][1]) * ang, st)
            right_eye_height = getStandard((max(facey) - ff['FSDKP_RIGHT_EYE'][1]) * ang, st)
            left_eye_length = getStandard((abs(ff['FSDKP_LEFT_EYEBROW_INNER_CORNER'][0] - ff['FSDKP_LEFT_EYEBROW_OUTER_CORNER'][0])) * ang, st)
            right_eye_length = getStandard((abs(ff['FSDKP_RIGHT_EYEBROW_INNER_CORNER'][0] - ff['FSDKP_RIGHT_EYEBROW_OUTER_CORNER'][0])) * ang, st)
            eye_distance = getStandard(abs(ff['FSDKP_LEFT_EYE'][0] - ff['FSDKP_RIGHT_EYE'][0]) * ang, st)
            nose_height = getStandard(abs(max(facey) - ff['FSDKP_NOSE_TIP'][1]) * ang, st)
            nose_width = getStandard(abs(ff['FSDKP_NOSE_LEFT_WING'][0] - ff['FSDKP_NOSE_RIGHT_WING'][0]) * ang, st)
            nose_length = getStandard((ff['FSDKP_NOSE_BRIDGE'][1] - ff['FSDKP_NOSE_TIP'][1]) * ang, st)
            chin_width = getStandard((ff['FSDKP_CHIN_LEFT'][0] - ff['FSDKP_CHIN_RIGHT'][0]) * ang, st)
            left_eyebrow_length = getStandard((ff['FSDKP_LEFT_EYEBROW_OUTER_CORNER'][0] - ff['FSDKP_LEFT_EYEBROW_INNER_CORNER'][0]) * ang, st)
            right_eyebrow_length = getStandard((ff['FSDKP_RIGHT_EYEBROW_OUTER_CORNER'][0] - ff['FSDKP_RIGHT_EYEBROW_INNER_CORNER'][0]) * ang, st)
            eyebrow_center_distance = getStandard((ff['FSDKP_LEFT_EYEBROW_MIDDLE'][0] - ff['FSDKP_RIGHT_EYEBROW_MIDDLE'][0]) * ang, st)
            left_eyebrow_height = getStandard((ff['FSDKP_LEFT_EYEBROW_MIDDLE'][1] - tl[1]) * ang, st)
            right_eyebrow_height = getStandard((ff['FSDKP_RIGHT_EYEBROW_MIDDLE'][1] - tl[1]) * ang, st)
            mouth_length = getStandard((ff['FSDKP_MOUTH_RIGHT_CORNER'][0] - ff['FSDKP_MOUTH_LEFT_CORNER'][0]) * ang, st)
            lip_thickness = getStandard((ff['FSDKP_MOUTH_TOP'][1] - ff['FSDKP_MOUTH_BOTTOM'][1]) * ang, st)
            mouth_height = getStandard(((ff['FSDKP_MOUTH_TOP'][1] + ff['FSDKP_MOUTH_BOTTOM'][1])/2.0 - max(facey)) * ang, st)
            FacialFeature(photo = self,
                          FSDKP_LEFT_EYE = ff['FSDKP_LEFT_EYE'],
                          FSDKP_RIGHT_EYE = ff['FSDKP_RIGHT_EYE'],
                          FSDKP_NOSE_TIP = ff['FSDKP_NOSE_TIP'],
                          FSDKP_MOUTH_RIGHT_CORNER = ff['FSDKP_MOUTH_RIGHT_CORNER'],
                          FSDKP_MOUTH_LEFT_CORNER = ff['FSDKP_MOUTH_LEFT_CORNER'],
                          FSDKP_FACE_CONTOUR7 = ff['FSDKP_FACE_CONTOUR7'],
                          FSDKP_FACE_CONTOUR6 = ff['FSDKP_FACE_CONTOUR6'],
                          FSDKP_FACE_CONTOUR8 = ff['FSDKP_FACE_CONTOUR8'],
                          FSDKP_FACE_CONTOUR5 = ff['FSDKP_FACE_CONTOUR5'],
                          FSDKP_FACE_CONTOUR9 = ff['FSDKP_FACE_CONTOUR9'],
                          FSDKP_FACE_CONTOUR4 = ff['FSDKP_FACE_CONTOUR4'],
                          FSDKP_FACE_CONTOUR10 = ff['FSDKP_FACE_CONTOUR10'],
                          FSDKP_FACE_CONTOUR1 = ff['FSDKP_FACE_CONTOUR1'],
                          FSDKP_FACE_CONTOUR13 = ff['FSDKP_FACE_CONTOUR13'],
                          FSDKP_CHIN_LEFT = ff['FSDKP_CHIN_LEFT'],
                          FSDKP_CHIN_RIGHT = ff['FSDKP_CHIN_RIGHT'],
                          FSDKP_LEFT_EYEBROW_OUTER_CORNER = ff['FSDKP_LEFT_EYEBROW_OUTER_CORNER'],
                          FSDKP_LEFT_EYEBROW_INNER_CORNER = ff['FSDKP_LEFT_EYEBROW_INNER_CORNER'],
                          FSDKP_RIGHT_EYEBROW_INNER_CORNER = ff['FSDKP_RIGHT_EYEBROW_INNER_CORNER'],
                          FSDKP_RIGHT_EYEBROW_OUTER_CORNER = ff['FSDKP_RIGHT_EYEBROW_OUTER_CORNER'],
                          FSDKP_MOUTH_TOP = ff['FSDKP_MOUTH_TOP'],
                          FSDKP_MOUTH_BOTTOM = ff['FSDKP_MOUTH_BOTTOM'],
                          FSDKP_NOSE_BRIDGE = ff['FSDKP_NOSE_BRIDGE'],
                          FSDKP_LEFT_EYE_OUTER_CORNER = ff['FSDKP_LEFT_EYE_OUTER_CORNER'],
                          FSDKP_LEFT_EYE_INNER_CORNER = ff['FSDKP_LEFT_EYE_INNER_CORNER'],
                          FSDKP_RIGHT_EYE_INNER_CORNER = ff['FSDKP_RIGHT_EYE_INNER_CORNER'],
                          FSDKP_RIGHT_EYE_OUTER_CORNER = ff['FSDKP_RIGHT_EYE_OUTER_CORNER'],
                          FSDKP_NOSE_LEFT_WING = ff['FSDKP_NOSE_LEFT_WING'],
                          FSDKP_NOSE_RIGHT_WING = ff['FSDKP_NOSE_RIGHT_WING'],
                          FSDKP_CHIN_BOTTOM = ff['FSDKP_CHIN_BOTTOM'],
                          FSDKP_FACE_CONTOUR2 = ff['FSDKP_FACE_CONTOUR2'],
                          FSDKP_FACE_CONTOUR12 = ff['FSDKP_FACE_CONTOUR12'],
                          FSDKP_FACE_CONTOUR3 = ff['FSDKP_FACE_CONTOUR3'],
                          FSDKP_FACE_CONTOUR11 = ff['FSDKP_FACE_CONTOUR11'],
                          FSDKP_LEFT_EYEBROW_MIDDLE = ff['FSDKP_LEFT_EYEBROW_MIDDLE'],
                          FSDKP_RIGHT_EYEBROW_MIDDLE = ff['FSDKP_RIGHT_EYEBROW_MIDDLE'],
                          FSDKP_MOUTH_LEFT_TOP = ff['FSDKP_MOUTH_LEFT_TOP'],
                          FSDKP_MOUTH_RIGHT_TOP = ff['FSDKP_MOUTH_RIGHT_TOP'],
                          FSDKP_MOUTH_LEFT_BOTTOM = ff['FSDKP_MOUTH_LEFT_BOTTOM'],
                          FSDKP_MOUTH_RIGHT_BOTTOM = ff['FSDKP_MOUTH_RIGHT_BOTTOM'],
                          head_height = head_height,
                          head_width = head_width,
                          left_eye_height = left_eye_height,
                          right_eye_height = right_eye_height,
                          left_eye_length = left_eye_length,
                          right_eye_length = right_eye_length,
                          eye_distance = eye_distance,
                          nose_height = nose_height,
                          nose_width = nose_width,
                          nose_length = nose_length,
                          chin_width = chin_width,
                          left_eyebrow_length = left_eyebrow_length,
                          right_eyebrow_length = right_eyebrow_length,
                          eyebrow_center_distance = eyebrow_center_distance,
                          left_eyebrow_height = left_eyebrow_height,
                          right_eyebrow_height = right_eyebrow_height,
                          mouth_length = mouth_length,
                          lip_thickness = lip_thickness,
                          mouth_height = mouth_height
                          )
            flush()
            im = Image.open(StringIO.StringIO(self.img)).copy()
            draw = ImageDraw.Draw(im)
            draw.ink = 0x00FF00
            for k in FacialFeature.names.values():
                draw.ellipse(getEllipse(ff[k]))
            draw.rectangle([tl, br])
            im = im.crop((min(facex)-20,
                          min(facey)-20,
                          max(facex)+20,
                          max(facey)+20))
            strio = StringIO.StringIO()
            im.save(strio,'JPEG')
            self.roi = strio.getvalue()
            os.remove(xmlfile)
            self.remove()
            flush()
            return 1

    def genSupervisedDataset(self):
        return [(
            self.feature.head_height,
            self.feature.head_width,
            self.feature.left_eye_height,
            self.feature.right_eye_height,
            self.feature.left_eye_length,
            self.feature.right_eye_length,
            self.feature.eye_distance,
            self.feature.nose_height,
            self.feature.nose_width,
            self.feature.nose_length,
            self.feature.chin_width,
            self.feature.left_eyebrow_length,
            self.feature.right_eyebrow_length,
            self.feature.eyebrow_center_distance,
            self.feature.left_eyebrow_height,
            self.feature.right_eyebrow_height,
            self.feature.mouth_length,
            self.feature.lip_thickness,
            self.feature.mouth_height
            #), (self.score,)]
            ), (int(round(self.score)),)]
        
class FacePosition(Entity):
    photo = ManyToOne('Photo')
    angle = Field(Float, default=0.0)
    w = Field(Float, default=0.0)
    center = Field(PickleType, default=(0,0))
    tl = Field(PickleType, default=(0,0))
    tr = Field(PickleType, default=(0,0))
    bl = Field(PickleType, default=(0,0))
    br = Field(PickleType, default=(0,0))

class FacialFeature(Entity):
    names = {0:'FSDKP_LEFT_EYE', 1:'FSDKP_RIGHT_EYE', 2:'FSDKP_NOSE_TIP', 3:'FSDKP_MOUTH_RIGHT_CORNER', 4:'FSDKP_MOUTH_LEFT_CORNER',
             5:'FSDKP_FACE_CONTOUR7', 6:'FSDKP_FACE_CONTOUR6', 7:'FSDKP_FACE_CONTOUR8', 8:'FSDKP_FACE_CONTOUR5', 9:'FSDKP_FACE_CONTOUR9',
             10:'FSDKP_FACE_CONTOUR4', 11:'FSDKP_FACE_CONTOUR10', 12:'FSDKP_FACE_CONTOUR1', 13:'FSDKP_FACE_CONTOUR13',
             14:'FSDKP_CHIN_LEFT', 15:'FSDKP_CHIN_RIGHT', 16:'FSDKP_LEFT_EYEBROW_OUTER_CORNER', 17:'FSDKP_LEFT_EYEBROW_INNER_CORNER',
             18:'FSDKP_RIGHT_EYEBROW_INNER_CORNER', 19:'FSDKP_RIGHT_EYEBROW_OUTER_CORNER', 20:'FSDKP_MOUTH_TOP', 21:'FSDKP_MOUTH_BOTTOM',
             22:'FSDKP_NOSE_BRIDGE', 23:'FSDKP_LEFT_EYE_OUTER_CORNER', 24:'FSDKP_LEFT_EYE_INNER_CORNER', 25:'FSDKP_RIGHT_EYE_INNER_CORNER',
             26:'FSDKP_RIGHT_EYE_OUTER_CORNER', 27:'FSDKP_NOSE_LEFT_WING', 28:'FSDKP_NOSE_RIGHT_WING', 29:'FSDKP_CHIN_BOTTOM',
             30:'FSDKP_FACE_CONTOUR2', 31:'FSDKP_FACE_CONTOUR12', 32:'FSDKP_FACE_CONTOUR3', 33:'FSDKP_FACE_CONTOUR11',
             34:'FSDKP_LEFT_EYEBROW_MIDDLE', 35:'FSDKP_RIGHT_EYEBROW_MIDDLE', 36:'FSDKP_MOUTH_LEFT_TOP', 37:'FSDKP_MOUTH_RIGHT_TOP',
             38:'FSDKP_MOUTH_LEFT_BOTTOM', 39:'FSDKP_MOUTH_RIGHT_BOTTOM'}
    photo = ManyToOne('Photo')
    FSDKP_LEFT_EYE = Field(PickleType, default=(0,0))
    FSDKP_RIGHT_EYE = Field(PickleType, default=(0,0))
    FSDKP_NOSE_TIP = Field(PickleType, default=(0,0))
    FSDKP_MOUTH_RIGHT_CORNER = Field(PickleType, default=(0,0))
    FSDKP_MOUTH_LEFT_CORNER = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR7 = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR6 = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR8 = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR5 = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR9 = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR4 = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR10 = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR1 = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR13 = Field(PickleType, default=(0,0))
    FSDKP_CHIN_LEFT = Field(PickleType, default=(0,0))
    FSDKP_CHIN_RIGHT = Field(PickleType, default=(0,0))
    FSDKP_LEFT_EYEBROW_OUTER_CORNER = Field(PickleType, default=(0,0))
    FSDKP_LEFT_EYEBROW_INNER_CORNER = Field(PickleType, default=(0,0))
    FSDKP_RIGHT_EYEBROW_INNER_CORNER = Field(PickleType, default=(0,0))
    FSDKP_RIGHT_EYEBROW_OUTER_CORNER = Field(PickleType, default=(0,0))
    FSDKP_MOUTH_TOP = Field(PickleType, default=(0,0))
    FSDKP_MOUTH_BOTTOM = Field(PickleType, default=(0,0))
    FSDKP_NOSE_BRIDGE = Field(PickleType, default=(0,0))
    FSDKP_LEFT_EYE_OUTER_CORNER = Field(PickleType, default=(0,0))
    FSDKP_LEFT_EYE_INNER_CORNER = Field(PickleType, default=(0,0))
    FSDKP_RIGHT_EYE_INNER_CORNER = Field(PickleType, default=(0,0))
    FSDKP_RIGHT_EYE_OUTER_CORNER = Field(PickleType, default=(0,0))
    FSDKP_NOSE_LEFT_WING = Field(PickleType, default=(0,0))
    FSDKP_NOSE_RIGHT_WING = Field(PickleType, default=(0,0))
    FSDKP_CHIN_BOTTOM = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR2 = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR12 = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR3 = Field(PickleType, default=(0,0))
    FSDKP_FACE_CONTOUR11 = Field(PickleType, default=(0,0))
    FSDKP_LEFT_EYEBROW_MIDDLE = Field(PickleType, default=(0,0))
    FSDKP_RIGHT_EYEBROW_MIDDLE = Field(PickleType, default=(0,0))
    FSDKP_MOUTH_LEFT_TOP = Field(PickleType, default=(0,0))
    FSDKP_MOUTH_RIGHT_TOP = Field(PickleType, default=(0,0))
    FSDKP_MOUTH_LEFT_BOTTOM = Field(PickleType, default=(0,0))
    FSDKP_MOUTH_RIGHT_BOTTOM = Field(PickleType, default=(0,0))
    head_height = Field(Float, default=100.0)
    head_width = Field(Float)
    left_eye_height = Field(Float)
    right_eye_height = Field(Float)
    left_eye_length = Field(Float)
    right_eye_length = Field(Float)
    eye_distance = Field(Float)
    nose_height = Field(Float)
    nose_width = Field(Float)
    nose_length = Field(Float)
    chin_width = Field(Float)
    left_eyebrow_length = Field(Float)
    right_eyebrow_length = Field(Float)
    eyebrow_center_distance = Field(Float)
    left_eyebrow_height = Field(Float)
    right_eyebrow_height = Field(Float)
    mouth_length = Field(Float)
    lip_thickness = Field(Float)
    mouth_height = Field(Float)

class AnnNets(Entity):
    netroot = os.path.join(basepath,'cap','ann')
    inputs = Field(Integer, default=0)
    hidden = Field(Integer, default=0)
    outputs = Field(Integer, default=0)
    filename = Field(String(100), default='')
    time = Field(DateTime, default=datetime.datetime.now())
    def getFile(self, name=''):
        if not name:
            return os.path.join(self.netroot,self.filename)
        else: return os.path.join(self.netroot, name)

class SVMNets(Entity):
    netroot = os.path.join(basepath,'cap','svm')
    inputs = Field(Integer, default=0)
    outputs = Field(Integer, default=0)
    filename = Field(String(100), default='')
    time = Field(DateTime, default=datetime.datetime.now())
    def getFile(self):
        return os.path.join(self.netroot,self.filename)
    
setup_all()

